package com.wei.czz.framework.index.controller;

import com.wei.czz.common.annotation.Resubmit;
import com.wei.czz.common.constant.Constant;
import com.wei.czz.common.dto.admin.UserAppealDto;
import com.wei.czz.common.dto.blog.*;
import com.wei.czz.common.dto.admin.userRisk.UserRiskDto;
import com.wei.czz.common.dto.blog.comment.CommentDto;
import com.wei.czz.common.enums.blog.BlogEnum;
import com.wei.czz.common.jsr303.group.OrderSearch;
import com.wei.czz.common.jsr303.group.Search;
import com.wei.czz.common.po.UserPo;
import com.wei.czz.common.tool.PageDto;
import com.wei.czz.common.tool.Result;
import com.wei.czz.common.utils.ContextUtils;
import com.wei.czz.common.utils.SecurityUtils;
import com.wei.czz.common.vo.blog.BlogCommentVo;
import com.wei.czz.common.vo.blog.IndexVo;
import com.wei.czz.common.vo.admin.UserAppealVo;
import com.wei.czz.framework.admin.manager.UserRiskManager;
import com.wei.czz.framework.blog.manager.BlogCommentManager;
import com.wei.czz.framework.common.service.AsyncService;
import com.wei.czz.framework.index.service.IndexService;
import com.wei.czz.framework.admin.service.UserAppealService;
import com.wei.czz.framework.admin.service.UserRiskFlowService;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2021-05-26 16:28:00
 * className: IndexController 浏览器页面-首页面-接口类
 * version: 1.0
 * description:
 */
@Validated
@RestController
@RequestMapping("index")
@AllArgsConstructor
public class IndexController {

    private static final Logger log = LoggerFactory.getLogger(IndexController.class);

    private final IndexService indexService;

    private final BlogCommentManager blogCommentManager;

    private final UserRiskFlowService userRiskFlowService;

    private final UserAppealService userAppealService;

    private final UserRiskManager userRiskManager;

    private final AsyncService asyncService;

    /**
     * 分页查询系统博客列表接口
     * 根据博客检索表单，分页查询博客列表
     * <p>
     * 调用方：
     *  1、浏览器-首页面调用
     * @param indexVo 首页面博客检索Vo类
     */
    @PostMapping("/blog/list")
    public Result<PageDto<BlogDto>> getIndexBlogList(@Validated(Search.class) @RequestBody IndexVo indexVo) {
        log.info("开始首页面博客列表分页查询业务。{}", indexVo);

        PageDto<BlogDto> page = indexService.getIndexBlogList(indexVo);

        log.info("首页面博客列表分页查询业务结束");
        return Result.success(page);
    }

    /**
     * 获取一个类型排行前十的博客列表接口
     * 根据博客类型，获取对应类型前十个博客
     * <p>
     * 调用方：
     *  1、浏览器-首页面调用
     * @param sortType 博客的排序类型[0-热门 1-今日发布 2-系统推荐]
     */
    @GetMapping("/sort/list")
    public Result<List<SortBlogDto>> getSortBlogList(@RequestParam("sortType") Integer sortType) {
        log.info("开始根据排序类型获取博客列表业务。sortType={}", sortType);

        BlogEnum blogEnum = BlogEnum.getTopTenBlogType(sortType);

        List<SortBlogDto> blogList = indexService.getSortBlogList(blogEnum);

        log.info("根据排序类型获取博客列表业务结束");
        return Result.success(blogList);
    }

    /**
     * 获取首页面展示的标签信息接口
     * 获取系统已发布博客引用标签数量排行前十的标签信息
     * <p>
     * 调用方：
     *  1、浏览器-首页面调用
     */
    @GetMapping("/tag/list")
    public Result<List<TagSortDto>> getTagList() {
        log.info("开始查询标签被已发布博客引用数量排行前十的标签信息业务");

        List<TagSortDto> tagList = indexService.getTopTenTagList();

        log.info("查询标签被已发布博客引用数量排行前十的标签信息业务结束");
        return Result.success(tagList);
    }

    /**
     * 系统统计接口
     * 获取系统的统计信息
     * <p>
     * 调用方：
     *  1、浏览器-首页面调用
     */
    @GetMapping("/stats")
    public Result<WebStatsDto> getWebStats() {
        log.info("开始获取系统统计信息业务");

        WebStatsDto webStats = indexService.getWebStats();

        log.info("获取系统统计信息业务结束");
        return Result.success(webStats);
    }

    /**
     * 获取博客详细信息接口
     * 根据博客id，获取博客详细信息
     * <p>
     * 调用方：
     *  1、浏览器-博客详细页面调用
     * @param blogCommentVo 封装请求参数的对象
     * @param request       http请求对象
     */
    @PostMapping("/blog/info")
    public Result<Map<String, Object>> getBlogInfo(
            @Validated(OrderSearch.class) @RequestBody BlogCommentVo blogCommentVo, HttpServletRequest request) {
        log.info("开始查询博客详细信息业务。{}", blogCommentVo);

        // 获取请求用户
        Optional<UserPo> optional = SecurityUtils.userPoOptional();
        optional.ifPresent(userPo -> blogCommentVo.setUserId(userPo.getUserId()));

        Future<PageDto<CommentDto>> future = asyncService.supplyAsync(() -> {
            log.info("开始异步查询博客第一级评论信息列表业务");
            PageDto<CommentDto> oneCommentList = blogCommentManager.getOneCommentList(blogCommentVo);
            log.info("异步查询博客第一级评论信息列表业务结束");
            return oneCommentList;
        });

        // 查询博客信息
        BlogDto blogDto = indexService.getBlogInfo(blogCommentVo.getBlogId());

        PageDto<CommentDto> page;
        try {
            page = future.get();
        } catch (InterruptedException | ExecutionException e) {
            log.error("异步查询第一级博客评论列表失败，请及时处理。message={}", e.getMessage(), e);
            return Result.fail("查询第一级博客评论列表失败，请联系管理员处理！！");
        }

        log.info("查询博客详细信息业务结束");
        Map<String, Object> data = new HashMap<>();
        data.put("blog", blogDto); data.put("page", page);
        return Result.success(data);
    }

    /**
     * 增加博客浏览量接口
     * 根据博客id，添加博客浏览量和博客点击量
     * <p>
     * 调用方：
     *  1、浏览器-博客详细页面调用
     * @param blogId  博客id
     */
    @GetMapping("/add/view")
    public Result<Object> addBlogView(@RequestParam("blogId") Long blogId) {
        log.info("开始增加博客浏览量业务。blogId={}", blogId);

        Long userId = ContextUtils.get(Constant.USER_ID);

        indexService.addBlogView(userId, blogId);

        log.info("增加博客浏览量业务结束");
        return Result.success();
    }

    /**
     * 用户获取自己的风控信息接口
     * 根据风控id，用户获取自己的风控信息
     * <p>
     * 调用方：
     *  1、浏览器-用户申诉页面调用
     *
     * @param riskRecordId 风控记录主键
     * @return 请求结果
     */
    @GetMapping("/user/risk")
    public Result<UserRiskDto> getMyRisk(@RequestParam("riskRecordId") Long riskRecordId) {
        log.info("开始用户获取自己的风控信息业务。riskRecordId={}", riskRecordId);

        UserRiskDto userRiskDto = userRiskFlowService.getMyRisk(riskRecordId);

        log.info("用户获取自己的风控信息业务结束");
        return Result.success(userRiskDto);
    }

    /**
     * 用户申诉接口
     * 保存用户申诉信息
     * <p>
     * 调用方：
     *  1、浏览器-用户申诉页面调用
     * <p>
     * 此接口会校验是否重复提交
     * @param userAppealVo 用户申诉请求参数对象
     * @return 请求结果
     */
    @Resubmit(message = "申诉提交")
    @PostMapping("/save/appeal")
    public Result<Boolean> saveAppeal(@Validated @RequestBody UserAppealVo userAppealVo) {
        log.info("开始用户申诉业务。{}", userAppealVo);

        Result<Boolean> result = userRiskManager.saveAppeal(userAppealVo);

        log.info("用户申诉业务结束");
        return result;
    }

    /**
     * 分页查询申诉记录列表接口
     * 根据风控主键和分页参数，分页查询用户自己的申诉记录列表
     * <p>
     * 调用方：
     *  1、浏览器-用户申诉页面调用
     *
     * @param riskFlowId 用户风控记录主键
     * @param page   查询页码
     * @param limit  页大小
     * @return 请求结果
     */
    @GetMapping("/appeal/list")
    public Result<PageDto<UserAppealDto>> getUserAppealList(@RequestParam("riskRecordId") Long riskFlowId,
                                                            @RequestParam("page") Integer page,
                                                            @RequestParam("limit") Integer limit) {
        log.info("开始分页查询申诉记录列表业务。riskFlowId={} page={} limit={}", riskFlowId, page, limit);

        PageDto<UserAppealDto> pageDto = userAppealService.getPageUserAppealList(riskFlowId, page, limit);

        log.info("分页查询申诉记录列表业务结束");
        return Result.success(pageDto);
    }

}
